/*
 * Copyright 2008-2023 dexian.vip. All rights reserved.
 * Support: http://www.dexian.vip
 * License: http://www.dexian.vip/license
 */

package vip.dexian.common.utils;

import vip.dexian.common.CommonAttributes;
import vip.dexian.common.query.Filter;
import vip.dexian.common.query.LocalDateTimeBetween;
import vip.dexian.common.query.LongBetween;
import vip.dexian.common.query.Operator;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * 查询条件组合 工具类
 *
 * @author 挺好的 2023年05月30日 18:53
 */
@Slf4j
public final class FilterUtils {

    /**
     * 创建时间
     */
    public static final String CREATE_DATE_PROPERTY_NAME = "createDate";

    /**
     * 参数名称（唯一索引标记）
     */
    public static final String UNIQUE_TIMESTAMP_NAME = "uniqueTimestamp";

    /**
     * id
     */
    public static final String ID_PROPERTY_NAME = "id";

    /**
     * 搜索条件
     */
    private final List <Filter> filters;

    private FilterUtils () {
        this.filters = new ArrayList <>();
    }

    /**
     * 创建一个Builder
     *
     * @return {@link FilterUtils}
     */
    public static FilterUtils builder () {
        return new FilterUtils();
    }

    /**
     * 添加between条件。属性名称为id
     *
     * @param begin
     *         开始ID
     * @param end
     *         结束ID
     *
     * @return {@link FilterUtils}
     */
    public FilterUtils betweenId (Long begin, Long end) {
        if (NumberUtils.isPositive(begin) && NumberUtils.isPositive(end)) {
            LongBetween longBetween = LongBetween.of(begin, end);
            return this.add(ID_PROPERTY_NAME, longBetween, Operator.BETWEEN);
        }

        return this;
    }


    /**
     * 添加between条件
     *
     * @param property
     *         属性名称
     * @param value
     *         属性的值
     *
     * @return {@link FilterUtils}
     */
    public FilterUtils between (String property, Object value) {
        return this.add(property, value, Operator.BETWEEN);
    }

    /**
     * 添加in条件。属性名称为id
     *
     * @param value
     *         属性的值
     *
     * @return {@link FilterUtils}
     */
    public FilterUtils inId (Object value) {
        return this.in(ID_PROPERTY_NAME, value);
    }

    /**
     * 添加in条件
     *
     * @param property
     *         属性名称
     * @param value
     *         属性的值
     *
     * @return {@link FilterUtils}
     */
    public FilterUtils in (String property, Object value) {
        return this.add(property, value, Operator.IN);
    }

    /**
     * 添加true条件，仅当 value != null && instanceof Boolean 的时候，条件才生效
     *
     * @param property
     *         属性名称
     * @param value
     *         属性的值
     *
     * @return {@link FilterUtils}
     */
    public FilterUtils isTrue (String property, Object value) {
        return this.add(property, value, Operator.IS_TRUE);
    }

    /**
     * 添加false条件，仅当 value != null && instanceof Boolean 的时候，条件才生效
     *
     * @param property
     *         属性名称
     * @param value
     *         属性的值
     *
     * @return {@link FilterUtils}
     */
    public FilterUtils isFalse (String property, Object value) {
        return this.add(property, value, Operator.IS_FALSE);
    }

    /**
     * 添加true或者false条件，当value != null ，根据value来添加是true还是false
     *
     * @param property
     *         属性名称
     * @param value
     *         属性的值
     *
     * @return {@link FilterUtils}
     */
    public FilterUtils isTrueOrFalse (String property, Object value) {

        if (value == null) {
            return this;
        }

        Boolean boolValue = (Boolean) value;

        return this.add(property, value, Boolean.TRUE.equals(boolValue) ? Operator.IS_TRUE : Operator.IS_FALSE);
    }

    /**
     * 是否为空
     *
     * @param property
     *         属性名称
     *
     * @return {@link FilterUtils}
     */
    public FilterUtils isNull (String property) {
        this.filters.add(Filter.isNull(property));
        return this;
    }

    /**
     * 是否不为空
     *
     * @param property
     *         属性名称
     *
     * @return {@link FilterUtils}
     */
    public FilterUtils isNotNull (String property) {
        this.filters.add(Filter.isNotNull(property));
        return this;
    }

    /**
     * 不等于
     *
     * @param property
     *         属性名称
     * @param value
     *         值
     *
     * @return {@link FilterUtils}
     */
    public FilterUtils notEquals (String property, Object value) {
        this.filters.add(Filter.ne(property, value));
        return this;
    }

    /**
     * 添加创建时间
     *
     * @param lowerBound
     *         开始时间
     * @param upperBound
     *         结束时间
     *
     * @return {@link FilterUtils}
     */
    public FilterUtils createDate (LocalDateTime lowerBound, LocalDateTime upperBound) {

        if (lowerBound == null || upperBound == null) {
            return this;
        }

        LocalDateTimeBetween localDateTimeBetween = LocalDateTimeBetween.of(lowerBound, upperBound);

        return this.date(CREATE_DATE_PROPERTY_NAME, localDateTimeBetween);
    }

    /**
     * 添加日期
     *
     * @param property
     *         日期属性
     * @param localDateTimeBetween
     *         日期范围
     *
     * @return {@link FilterUtils}
     */
    public FilterUtils date (String property, LocalDateTimeBetween localDateTimeBetween) {
        return this.add(property, localDateTimeBetween, Operator.BETWEEN);
    }

    /**
     * 添加like条件。 右匹配。如LIKE ''天猪%
     *
     * @param property
     *         属性名称
     * @param value
     *         值
     *
     * @return {@link FilterUtils}
     */
    public FilterUtils rightLike (String property, Object value) {
        return this.add(property, value, Operator.RIGHT_LIKE);
    }

    /**
     * 添加like条件。 左右匹配。如LIKE ''%天猪%
     *
     * @param property
     *         属性名称
     * @param value
     *         值
     *
     * @return {@link FilterUtils}
     */
    public FilterUtils like (String property, Object value) {
        return this.add(property, value, Operator.LIKE);
    }

    /**
     * 添加相等条件
     *
     * @param property
     *         属性名称
     * @param value
     *         值
     *
     * @return {@link FilterUtils}
     */
    public FilterUtils equals (String property, Object value) {
        return this.add(property, value, Operator.EQ);
    }

    /**
     * 是否大于某个数字
     *
     * @param property
     *         属性名称
     * @param value
     *         属性值
     *
     * @return {@link FilterUtils}
     */
    public FilterUtils gt (String property, Object value) {
        return this.add(property, value, Operator.GT);
    }

    /**
     * 是否大于等于某个数字
     *
     * @param property
     *         属性名称
     * @param value
     *         属性值
     *
     * @return {@link FilterUtils}
     */
    public FilterUtils ge (String property, Object value) {
        return this.add(property, value, Operator.GE);
    }

    /**
     * 是否小于等于某个数字
     *
     * @param property
     *         属性名称
     * @param value
     *         属性值
     *
     * @return {@link FilterUtils}
     */
    public FilterUtils le (String property, Object value) {
        return this.add(property, value, Operator.LE);
    }

    /**
     * 添加or条件，只针对单个属性多个值
     *
     * @param property
     *         属性名称
     * @param value
     *         属性值 {@link Filter}
     *
     * @return {@link FilterUtils}
     */
    public FilterUtils or (String property, Object value) {
        return this.add(property, value, Operator.OR);
    }

    /**
     * 添加唯一索引
     *
     * @return {@link FilterUtils}
     */
    public FilterUtils uniqueKey () {
        Filter filter = Filter.eq(UNIQUE_TIMESTAMP_NAME, CommonAttributes.UniqueKeySetting.DEFAULT_UNIQUE_TIMESTAMP);
        return this.add(filter);
    }

    /**
     * 添加Filter，系统不会检测条件是否重复（即property一样），在添加条件的时候一定要注意是否添加了多个一样的条件。
     * <p>i
     * 另外需要注意的是在创建创建Filter的时候会校验Filter是否有效（{@link Filter#isValid}），只有有效的才会加入到条件。
     *
     * @param filter
     *         查询条件
     *
     * @return {@link FilterUtils}
     */
    public FilterUtils add (Filter filter) {

        if (filter == null || !filter.isValid()) {
            log.debug("filter({}) is null  is not valid!", filter);
            return this;
        }

        this.filters.add(filter);

        return this;
    }

    /**
     * 返回查询条件列表
     *
     * @return 查询条件列表
     */
    public List <Filter> build () {
        return this.filters;
    }

    /**
     * 检测是否有效
     *
     * @param property
     *         属性名称
     * @param value
     *         值。值得校验{@link org.apache.commons.lang3.ObjectUtils#isNotEmpty}
     *
     * @return property is empty || value is empty , return false.
     */
    private boolean isValid (String property, Object value) {
        return StringUtils.isNotEmpty(property) && ObjectUtils.isNotEmpty(value);
    }


    /**
     * 添加搜索条件
     *
     * @param property
     *         属性名称
     * @param value
     *         值
     * @param operator
     *         运算符。{@link Operator}
     *
     * @return {@link FilterUtils}
     */
    private FilterUtils add (String property, Object value, Operator operator) {

        log.debug("property({}), value({}), operator({})", property, value, operator);

        if (!this.isValid(property, value)) {
            log.debug("property value {} is empty or value {} is empty!", property, value);
            return this;
        }

        Filter filter;

        switch (operator) {
            case EQ:
                filter = Filter.eq(property, value);
                break;
            case NE:
                filter = Filter.ne(property, value);
                break;
            case GT:
                filter = Filter.gt(property, value);
                break;
            case LT:
                filter = Filter.lt(property, value);
                break;
            case GE:
                filter = Filter.ge(property, value);
                break;
            case LE:
                filter = Filter.le(property, value);
                break;
            case IN:
                filter = Filter.in(property, value);
                break;
            case BETWEEN:
                filter = Filter.between(property, value);
                break;
            case IS_TRUE:
                filter = Filter.isTrue(property, value);
                break;
            case IS_FALSE:
                filter = Filter.isFalse(property, value);
                break;
            case LIKE:
                filter = Filter.like(property, value);
                break;
            case RIGHT_LIKE:
                filter = Filter.likeRight(property, value);
                break;
            case OR:
                filter = Filter.or(property, value);
                break;
            default:
                filter = null;
        }
        return this.add(filter);
    }
}
